﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;

namespace WooCoo.ObjectModel.Validation
{
    /// <summary>
    /// 基于ValidationAttribute的数据验证，<see cref="System.ComponentModel.DataAnnotations"/>
    /// </summary>
    [Serializable]
    public class ValidationRules
    {
        [NonSerialized]
        private Dictionary<string, ValidationAttribute[]> _validators;

        private BrokenRulesCollection _brokenRules;
        public BrokenRulesCollection BrokenRules
        {
            get
            {
                if (_brokenRules == null)
                    _brokenRules = new BrokenRulesCollection();
                return _brokenRules;
            }
        }
        /// <summary>
        /// reference to current business object
        /// </summary>
        [NonSerialized]
        private Entity _target;
        internal Entity Target
        {
            get { return _target; }
        }
        /// <summary>
        /// 
        /// </summary>
        public ValidationRules(Entity entity)
        {
            SetTarget(entity);            
        }
        /// <summary>
        /// 在做序列化后，重设ValicationRules关联
        /// </summary>
        /// <param name="entity"></param>
        internal void SetTarget(Entity entity)
        {
            this._target = entity;
            _validators = new Dictionary<string, ValidationAttribute[]>();            
            AddDataAnnotations(typeof(Entity));
        }

        #region 加载数据验证
        private void AddDataAnnotations(Type type)
        {
            Type metadataType;
            //判断是否有MetadataTypeAttribute,如果是类与数据模型分离，则取元数据指向的类
            var classAttList = _target.GetType().GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.MetadataTypeAttribute), true);
            if (classAttList.Length > 0)
            {
                //MetadataTypeAttribute只能标记一个类，所以只需要取第一个。
                metadataType = ((System.ComponentModel.DataAnnotations.MetadataTypeAttribute)classAttList[0]).MetadataClassType;
                AddDataAnnotationsFromType(metadataType);
            }
            metadataType = _target.GetType();
            AddDataAnnotationsFromType(metadataType);
        }

        /// <summary>
        /// 从类型定义加载DataAnnotations
        /// </summary>
        /// <param name="metadataType">Type of the metadata.</param>
        private void AddDataAnnotationsFromType(Type metadataType)
        {
            // 只取字段的属性标记
            var propList = metadataType.GetProperties();
            foreach (var prop in propList)
            {
                var attList = prop.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute), true);
                if (attList.Count() != 0)
                    _validators.Add(prop.Name, attList as ValidationAttribute[]);
            }
        }
        #endregion

        /// <summary>
        /// 检查某个属性是否满足规则
        /// </summary>
        /// <param name="property">托管属性</param>
        public bool Validate(string propertyName)
        {
            if (!_validators.ContainsKey(propertyName))
                return true;
            var property = this._target.Properties.GetProperties().First(e => e.Name == propertyName);
            this.BrokenRules.ClearBrokenRules(property.Name);
            var value = this._target.GetProperty(property);            
            var errors = _validators[property.Name].Where(v => !v.IsValid(value))
                        .Select(v => new BrokenRule { PropertyName = property.Name, ErrorMessage = v.ErrorMessage });
            if (errors.Count() > 0)
            {
                foreach (var item in errors)
                    this.BrokenRules.Add(item);
                return false;
            }
            return true;
        }
        /// <summary>
        /// 检查该对象的所有规则
        /// </summary>
        /// <returns></returns>
        public void CheckRules()
        {
            foreach (var item in this._validators)
            {
                this.Validate(item.Key);
            }
        }

        /// <summary>
        /// 取得某一属性的错误信息
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        public string GetErrorMessage(string propertyName)
        {
            var errList = this.BrokenRules.Where(e => e.PropertyName == propertyName).Select(e => e.ErrorMessage);
            return string.Join(Environment.NewLine, errList);
        }
    }
}
